/*************************************************************************
 * Copyright 2008 David Almilli
 *  
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at 
 *  
 *     http://www.apache.org/licenses/LICENSE-2.0 
 *      
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 * See the License for the specific language governing permissions and 
 * limitations under the License.
 **************************************************************************/
package com.tivo.hme.sdk;

import com.tivo.hme.interfaces.IApplication;
import com.tivo.hme.interfaces.IArgumentList;
import com.tivo.hme.interfaces.IContext;
import com.tivo.hme.interfaces.IFactory;
import com.tivo.hme.interfaces.ILogger;
import com.tivo.hme.sdk.io.HmeInputStream;
import com.tivo.hme.sdk.io.HmeOutputStream;
import java.io.IOException;

public class FactoryPlus extends Factory {
    
    private boolean compatibilityMode;

    public FactoryPlus() {
        this(false);
    }
    public FactoryPlus(boolean compatibilityMode) {
        this.compatibilityMode = compatibilityMode;
    }

    public IApplication createApplication(IContext context) throws IOException {
        int version = -1;
        HmeOutputStream out = null;
        HmeInputStream in = null;

        synchronized (lock) { // lock this factory
            if (context.getOutputStream() instanceof HmeOutputStream) {
                out = (HmeOutputStream) context.getOutputStream();
            } else {
                out = new HmeOutputStream(context.getOutputStream());
            }

            if (context.getInputStream() instanceof HmeInputStream) {
                in = (HmeInputStream) context.getInputStream();
            } else {
                in = new HmeInputStream(context.getInputStream());
            }

            // HME protocol starts here
            // Synchronize and flush the writes, so that no other writer 
            // interleaves within our data
            synchronized (out.getMonitor()) {
                out.writeInt(IHmeProtocol.MAGIC);
                out.writeInt(getProtocolVersion());
                out.flush();
            }

            // read magic and version
            int magic = in.readInt();
            if (magic != IHmeProtocol.MAGIC) {
                throw new IOException("bad magic: 0x" + Integer.toHexString(magic)
                        + getIdForException());
            }
            version = in.readInt();
            if (version >> 8 < IHmeProtocol.VERSION >> 8) {
                throw new IOException("version mismatch: " + (version >> 8) + "."
                        + (version & 0xff) + " < " + (IHmeProtocol.VERSION >> 8) + "."
                        + (IHmeProtocol.VERSION & 0xff) + getIdForException());
            }
        } // synchronized

        // maintain proper order of locks -- we have released 
        // the factory monitor before entering the app monitor

        IApplication retApp = null;
        try {
            Application app = (Application) clazz.newInstance();
            synchronized (app) {
                app.setFactory(this);
                app.setContext(context, version);
                app.setMonitor(app);
                retApp = app;
            }
        } catch (InstantiationException ex) {
            log(ILogger.LOG_NOTICE, ex);
        } catch (IllegalAccessException ex) {
            log(ILogger.LOG_NOTICE, ex);
        }

        out.flush();

        return retApp;
    }
    

    public void initFactory(String appClassName, ClassLoader loader, IArgumentList args) {
        super.initFactory(appClassName, loader, args);
        if (compatibilityMode) {
            getFactoryData().put(IFactory.HME_VERSION_TAG, "0.41");
        }
    }
    
    protected int getProtocolVersion() {
        Object versionObj = getFactoryData().get(IFactory.HME_VERSION_TAG);
        if (versionObj instanceof String && !versionObj.equals(IHmeProtocol.VERSION_STRING)) {
            //it's an overridden version
            String[] parts = versionObj.toString().split("\\.");
            if (parts.length == 2) {
                int version = (Integer.parseInt(parts[0]) << 8) | Integer.parseInt(parts[1]);
                return version;
            }
        }
        return IHmeProtocol.VERSION;
    }
}
